#include "sdcard.h"
#include "LPC24xx.h"
#include "type.h"
#include "uart.h"
#include "debug_settings.h"
#include "irq.h"
#include "dma.h"
#include <string.h>

DWORD IRQHandlerCounter = 0;

volatile DWORD MCI_DataErrorProcess_count = 0;
volatile DWORD MCI_DATA_END_InterruptService_count = 0;
volatile DWORD MCI_FIFOInterruptService_count = 0;
volatile DWORD MCI_CmdProcess_count = 0;

volatile DWORD CmdCRCErrCount = 0;
volatile DWORD CmdTimeoutErrCount = 0;
volatile DWORD CmdRespEndCount = 0;
volatile DWORD CmdSentCount = 0;
volatile DWORD CmdActiveCount = 0;

volatile DWORD DataCRCErrCount = 0;
volatile DWORD DataTimeoutErrCount = 0;
volatile DWORD DataTxUnderrunErrCount = 0;
volatile DWORD DataRxOverrunErrCount = 0;
volatile DWORD DataStartbitErrCount = 0;

volatile DWORD DataEndCount = 0;
volatile DWORD DataBlockEndCount = 0;
volatile DWORD MCI_Block_End_Flag = 0;

volatile DWORD DataTxActiveCount = 0;
volatile DWORD DataRxActiveCount = 0;

volatile DWORD DataFIFOCount = 0;
volatile DWORD DataRxFIFOCount = 0;
volatile DWORD DataTxFIFOCount = 0;

volatile DWORD CardRCA;
volatile DWORD MCI_CardType;

/* Assembly modules using the ARM block transfer instruction in readfifo.s. */
// mthomas - volatile
extern DWORD MCI_ReadFifo(volatile DWORD * dest);
extern DWORD MCI_WriteFifo(volatile DWORD * src);

//volatile BYTE SRC_REG[4096];
//volatile BYTE DST_REG[4096];

// mthomas
#ifdef __GNUC__
volatile BYTE *WriteBlock __attribute__ ((aligned (4))) = (volatile BYTE *)(DMA_SRC); // treat WriteBlock as a constant address
volatile BYTE *ReadBlock __attribute__ ((aligned (4))) = (volatile BYTE *)(DMA_DST); // treat ReadBlock as a constant address
#else
volatile BYTE *WriteBlock = (BYTE *)(DMA_SRC); // treat WriteBlock as a constant address
volatile BYTE *ReadBlock = (BYTE *)(DMA_DST); // treat ReadBlock as a constant address
#endif

volatile DWORD TXBlockCounter=0, RXBlockCounter=0;
// mthomas
volatile DWORD SDStatRead = 0;



void SDCARD_init(void) {
	DWORD i;
#if DEBUG_SDCARD
	xprintf("Starting SDCARD_init()...\n");
#endif

	// Step 1. Set PC_MCI in PCONP; in Datasheet it's labeled as PCSDC... seems cool though.
	PCONP = (PCONP | (1 << 28));

	// Step 2.

	// PCKLSEL1 = CCLK = 72 = 72MHz -- Solid.
	//PCLKSEL1 |= (1 << 24);

	//PCLKSEL1 = CCLK/2 = 72/2 = 36 MHz. -- Questionable.
	//PCLKSEL1 |= (1 << 25);

	// PCLKSEL1 = CCLK/4 = 72/4 = 18 MHz. -- Possibly bad.
	//PCLKSEL1 |= (~(1 << 25) | ~(1<<24));

	// PCLKSEL1 = CCLK/8 = 72/8 = 9 MHz -- Bad.
	//PCLKSEL1 |= ((1 << 25) | (1 << 24));

	// Step 3. select SD/MMC pins and their modes in PINSEL0 - PINSEL4, PINMODE0 - PINMODE4, chose PINSEL2
	// PINSEL2
	/*
	 * [5:4] 	10 	MCICLK
	 * [7:6]	10	MCICMD
	 * [11:10]	10	MCIPWR
	 * [13:12] 	10	MCIDAT0
	 * [15:14]	10	MCIDAT1
	 * [23:22]	10	MCIDAT2
	 * [25:24]	10	MACIDAT3
	 *
	 * [31 30 29 28] [27 26 25 24] [23 22 21 20] [19 18 17 16] [15 14 13 12] [11 10 9 8] [7 6 5 4] [3 2 1 0]
	 *       0			     1 0     1 0  0  0         0         1  0  1 0    1  0  0 0   1 0 1 0       0
	 *       0x0280A8A0
	 */
	PINSEL2 |= 0x0280A8A0;
	//PINMODE2 |= 0x0280A8A0;

	PINMODE2 |= 0x00000800;

	//PINSEL1 |= 0x00002A80;        /* MCI-CLK,-CMD,-PWR,-DAT0*/
	//PINSEL4 |= 0x0A800000;        /* MCI-DAT1,-DAT2,-DAT3 */
	//PINMODE1 = 0;
	//PINMODE4 = 0;

	//MCI_DATA_LEN = 0x200;

	//OK now we jump into our implementation of the NXP example init.
	//first lets turn off the MCI clock and power.
	if ( MCI_CLOCK & (1 << 8) )	{
		MCI_CLOCK &= ~(1 << 8);
	}
	if ( MCI_POWER & 0x02 ) {
		MCI_POWER = 0x00;
	}

	//now busy wait for some reason
	for ( i = 0; i < 0x1000; i++ );

	/* Disable all interrupts for now */
	MCI_MASK0 = 0;
	MCI_MASK1 = MCI_MASK0;

	/*set up clocking default mode, clear any registers as needed */
	MCI_COMMAND = 0;
	MCI_DATA_CTRL = 0;
	MCI_CLEAR = 0x7FF;		/* clear all pending interrupts */

	MCI_POWER = 0x02;		/* power up */
	while ( !(MCI_POWER & 0x02) );
	for ( i = 0; i < 0x100; i++ );

	/* During identification phase, the clock should be less than
	400Khz. Once we pass this phase, the normal clock can be set up
	to 25Mhz on SD card and 20Mhz on MMC card. */
	MCI_Set_MCIClock( SLOW_RATE );
	MCI_POWER |= 0x03;		/* Power up to power on. */

	for ( i = 0; i < 0x2000; i++ );

	if ( install_irq( MCI_INT, (void *)MCI_IRQHandler, HIGHEST_PRIORITY ) == FALSE ) {
#if DEBUG_SDCARD
		xprintf("Error: Couldnt install_irq for MCI_IRQHandler.\n");
#endif
		return;
	}

	/*
	During the initialization phase, to simplify the process, the CMD related
	interrupts are disabled. The DATA related interrupts are enabled when
	the FIFOs are used and just before WRITE_BLOCK READ_BLOCK cmds are issues, and
	disabled after the data block has been written and read. Please also note,
	before WRITE_BLOCK only TX related data interrupts are enabled, and before
	READ_BLOCK only RX related data interrupts are enabled. */

	return;
}

DWORD SDCARD_identify() {
	DWORD i;
#if DEBUG_IDENTIFY_SDCARD
	xprintf("SD/MMC card: ");
#endif

	i = MCI_CardInit();

#if DEBUG_IDENTIFY_SDCARD
	if (i == CARD_UNKNOWN) {
		xprintf("UNKNOWN\n");
	}
	else if (i == MMC_CARD) {
		xprintf("MMC\n");
	}
	else if (i == SD_CARD) {
		xprintf("SD Card\n");
	}
	else if (i == SDSC_CARD) {
		xprintf("SDSC Card\n");
	}
	else if (i == SDHC_CARD) {
		xprintf("SDHC Card\n");
	}
	else if (i == SDV2_CARD) {
		xprintf("SDV2 (possibly problematic.)\n");
	}
	else {
		xprintf("MCI_CardInit() returned something way off.\n");
	}
#endif
	return i;
}

void SDCARD_test(void) {
	DWORD cardType;
	DWORD i, j;

	/* Fill block data pattern in write buffer and clear everything
	in read buffer. */
	for ( i = 0; i < BLOCK_LENGTH; i++ )
	{
		WriteBlock[i] = i;
		ReadBlock[i] = 0;
	}

#if MCI_DMA_ENABLED // initialize DMA
	/* on DMA channel 0, source is memory, destination is MCI FIFO. */
	/* On DMA channel 1, source is MCI FIFO, destination is memory. */
	DMA_Init();
#if DEBUG_SDCARD_DMA
	xprintf("DMA enabled\n");
#endif
#endif


	cardType = SDCARD_identify();


	if (MCI_Check_CID() != TRUE) {
		xprintf("Check_CID failed\n");while ( 1 );		/* fatal error */
	}

	if ( MCI_Set_Address() != TRUE ) {
			xprintf("Set_Address failed\n");while ( 1 );		/* fatal error */
		}

	if ( MCI_Send_CSD() != TRUE ) {
		xprintf("Send_CSD failed\n");while ( 1 );		/* fatal error */
	}

	if ( MCI_Select_Card() != TRUE ) {
		xprintf("Select_Card failed\n");while ( 1 );		/* fatal error */
	}

	if ( MCI_CardType == SD_CARD )
	{
		MCI_Set_MCIClock( NORMAL_RATE );

#if DEBUG_SDCARD
		xprintf("\nSet to normal rate.\n\n");
#endif

		if (SD_Set_BusWidth( SD_4_BIT ) != TRUE )
		{
			xprintf("set 4 bit mode failed\n");
			while ( 1 );	/* fatal error */
		}

	}

	if ( MCI_Set_BlockLen( BLOCK_LENGTH ) != TRUE )
	{
		xprintf("Set_BlockLen failed\n");
		while ( 1 );		/* fatal error */
	}

	/*********************************************
	 * Read/write test.
	 * Initialization is done by this point.
	 * *******************************************
	 */

	/* Start test */

	xprintf("SDCard test start: ");

	for ( i = 0; i < BLOCK_NUM; i++ )
	{
		//xprintf("Block %d ...", i);

		if ( MCI_Write_Block( i ) != TRUE )
		{
			xprintf("Write_Block failed\n");
			while ( 1 );		/* Fatal error */
		}
#if DEBUG_SDCARD
		else {
			xprintf("MCI_Write_Block #%d succeeded\n", i);
			xprintf("GPDMA_CH0_CFG = %08x\n", GPDMA_CH0_CFG);
			xprintf("GPDMA_CH0_CTRL = %08x\n", GPDMA_CH0_CTRL);
		}
#endif
		/* When MCI_Block_End_Flag is clear, it indicates
		   Write_Block is complete, next task, Read_Block to check write */
		while ( MCI_Block_End_Flag == 1 );
#if DEBUG_SDCARD
		xprintf("Got past MCI_Block_End_Flag\n");
#endif

		if ( MCI_Read_Block( i ) != TRUE )
		{
			xprintf("Read_Block failed\n");
			while ( 1 );		/* Fatal error */
		}
#if DEBUG_SDCARD
		else {
			xprintf("MCI_Read_Block #%d succeeded\n", i);
		}
#endif

		/* When MCI_Block_End_Flag is clear, it indicates RX is done
		   with Read_Block,  validation of RX and TX buffers next. */
		while ( MCI_Block_End_Flag == 1 ) ;

		for ( j = 0; j < (BLOCK_LENGTH); j++ )
		{
			if ( WriteBlock[j] != ReadBlock[j] )
			{
				xprintf("Compare failed\n");
				xprintf("WriteBlock[%d]: %02x\tReadBlock[%d]: %02x\n", j, WriteBlock[j], j, ReadBlock[j]);
				while ( 1 );	/* Data comparison failure, fatal error */
			}
		}
		//xprintf("o.k.\n");

		/*  clear read buffer for next block comparison */
		for ( j = 0; j < (BLOCK_LENGTH); j++ )
		{
			ReadBlock[j] = 0;
		}

		/*if ( i == 2 ) {
			// dump_status("before dump_csd");
			dump_csd();
			dump_sdstat();
		}*/
	}

	xprintf("ok!\n");

	return;
}


/******************************************************************************
 ** Function name:		MCI_Interrupt related
 **
 ** Descriptions:		MCI interrupt handler and related APIs
 **
 **
 ** parameters:			None
 ** Returned value:		None
 **
 ******************************************************************************/
void MCI_TXEnable(void) {
#if MCI_DMA_ENABLED
	MCI_MASK0 |= ((DATA_END_INT_MASK)|(ERR_TX_INT_MASK));
	/* Enable TX interrupts only */
	MCI_MASK1 |= ((DATA_END_INT_MASK)|(ERR_TX_INT_MASK));
	/* Enable TX interrupts only */
#else
	MCI_MASK0 |= ((FIFO_TX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* FIFO TX interrupts only */
	MCI_MASK1 |= ((FIFO_TX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* FIFO TX interrupts only */
#endif
	return;
}
/*****************************************************************/

void MCI_TXDisable(void) {
#if MCI_DMA_ENABLED
	MCI_MASK0 &= ~((DATA_END_INT_MASK)|(ERR_TX_INT_MASK));
	/* Enable TX interrupts only */
	MCI_MASK1 &= ~((DATA_END_INT_MASK)|(ERR_TX_INT_MASK));
	/* Enable TX interrupts only */
#else
	MCI_MASK0 &= ~((FIFO_TX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* FIFO TX interrupts only */
	MCI_MASK1 &= ~((FIFO_TX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_TX_INT_MASK)); /* FIFO TX interrupts only */
#endif
	return;
}
/*****************************************************************/

void MCI_RXEnable(void) {
#if MCI_DMA_ENABLED
	MCI_MASK0 |= ((DATA_END_INT_MASK)|(ERR_RX_INT_MASK));
	/* Enable RX interrupts only */
	MCI_MASK1 |= ((DATA_END_INT_MASK)|(ERR_RX_INT_MASK));
	/* Enable RX interrupts only */
#else
	MCI_MASK0 |= ((FIFO_RX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* FIFO RX interrupts only */
	MCI_MASK1 |= ((FIFO_RX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* FIFO RX interrupts only */
#endif
	return;
}
/*****************************************************************/

void MCI_RXDisable(void) {
#if MCI_DMA_ENABLED
	MCI_MASK0 &= ~((DATA_END_INT_MASK)|(ERR_RX_INT_MASK));
	/* Enable TX interrupts only */
	MCI_MASK1 &= ~((DATA_END_INT_MASK)|(ERR_RX_INT_MASK));
	/* Enable TX interrupts only */
#else
	MCI_MASK0 &= ~((FIFO_RX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* FIFO TX interrupts only */
	MCI_MASK1 &= ~((FIFO_RX_INT_MASK)|(DATA_END_INT_MASK)|(ERR_RX_INT_MASK)); /* FIFO TX interrupts only */
#endif
	return;
}


/******************************************************************************
 ** Function name:		MCI_CmdProcess
 **
 ** Descriptions:		Called by MCI interrupt handler
 **						To simplify the process, for card initialization, the
 **						CMD interrupts are disabled.
 **
 **
 ** parameters:			None
 ** Returned value:		None
 **
 ******************************************************************************/
// mthomas - static
static void MCI_CmdProcess(void) {
	DWORD MCIStatus;

	MCIStatus = MCI_STATUS;

	if (MCIStatus & MCI_CMD_CRC_FAIL) {
		CmdCRCErrCount++;
		MCI_CLEAR = MCI_CMD_CRC_FAIL;
	}
	if (MCIStatus & MCI_CMD_TIMEOUT) {
		CmdTimeoutErrCount++;
		MCI_CLEAR = MCI_CMD_TIMEOUT;
	}
	/* Cmd Resp End or Cmd Sent */
	if (MCIStatus & MCI_CMD_RESP_END) {
		CmdRespEndCount++;
		MCI_CLEAR = MCI_CMD_RESP_END;
	}
	if (MCIStatus & MCI_CMD_SENT) {
		CmdSentCount++;
		MCI_CLEAR = MCI_CMD_SENT;
	}
	if (MCIStatus & MCI_CMD_ACTIVE) {
		CmdActiveCount++;
		MCI_CLEAR = MCI_CMD_ACTIVE;
	}
	return;
}

/******************************************************************************
 ** Function name:		MCI_DataErrorProcess
 **
 ** Descriptions:		Called by MCI interrupt handler
 **						Process data error.
 **
 ** parameters:			None
 ** Returned value:		None
 **
 ******************************************************************************/
// mthomas - static
static void MCI_DataErrorProcess(void) {
	DWORD MCIStatus;

	MCIStatus = MCI_STATUS;

	if (MCIStatus & MCI_DATA_CRC_FAIL) {
#if DEBUG_SDCARD_INTERRUPT_ERRORS
		xprintf("--> More specifically: MCI_DATA_CRC_FAIL\n");
		DWORD status;
		status = MCI_Send_Status();
		xprintf("MCI Status: %08x\n", status);
#endif
		DataCRCErrCount++;
		MCI_CLEAR = MCI_DATA_CRC_FAIL;
	}
	if (MCIStatus & MCI_DATA_TIMEOUT) {
#if DEBUG_SDCARD_INTERRUPT_ERRORS
		xprintf("--> More specifically: MCI_DATA_TIMEOUT\n");
	#if DEBUG_SDCARD_SALT
			xprintf("MCI Status after MCI_TX_UNDERRUN: variable: %08x // MCI_FIFO_CNT: %08x\n", MCI_STATUS, MCI_FIFO_CNT);
	#endif
#endif
		DataTimeoutErrCount++;
		MCI_CLEAR = MCI_DATA_TIMEOUT;
	}
	/* Underrun or overrun */
	if (MCIStatus & MCI_TX_UNDERRUN) {
#if DEBUG_SDCARD_INTERRUPT_ERRORS
		xprintf("--> More specifically: MCI_TX_UNDERRUN\n");
		xprintf("MCI Status after MCI_TX_UNDERRUN: variable: %08x // MCI_FIFO_CNT: %08x\n", MCI_STATUS, MCI_FIFO_CNT);
#endif
		DataTxUnderrunErrCount++;
		MCI_CLEAR = MCI_TX_UNDERRUN;
	}
	if (MCIStatus & MCI_RX_OVERRUN) {
#if DEBUG_SDCARD_INTERRUPT_ERRORS
		xprintf("--> More specifically: MCI_RX_OVERRUN\n");
#endif
		DataRxOverrunErrCount++;
		MCI_CLEAR = MCI_RX_OVERRUN;
	}
	/* Start bit error on data signal */
	if (MCIStatus & MCI_START_BIT_ERR) {
#if DEBUG_SDCARD_INTERRUPT_ERRORS
		xprintf("--> More specifically: MCI_START_BIT_ERR\n");
#endif
		DataStartbitErrCount++;
		MCI_CLEAR = MCI_START_BIT_ERR;
	}
	return;
}

/******************************************************************************
 ** Function name:		MCI_DATA_END_InterruptService
 **
 ** Descriptions:		Called by MCI interrupt handler
 **						This is the last interrupt module processing
 **						the block write and	read to and from the MM card.
 **
 **						FIFO interrupts are also used when DMA is disabled
 **						This routine simply clears the
 **						MCI_Block_End_Flag, and increments counters for debug
 **
 ** parameters:			None
 ** Returned value:		None
 **
 ******************************************************************************/
// mthomas - static
static void MCI_DATA_END_InterruptService(void) {
	DWORD MCIStatus;

	MCIStatus = MCI_STATUS;

	if (MCIStatus & MCI_DATA_END) /* Data end, and Data block end  */
	{
		DataEndCount++;
		MCI_CLEAR = MCI_DATA_END;
		return;
	}
	if (MCIStatus & MCI_DATA_BLK_END) {
		DataBlockEndCount++;
		MCI_CLEAR = MCI_DATA_BLK_END;
		MCI_TXDisable();
		MCI_Block_End_Flag = 0;
		return;
	}

	/* Tx active  */
	if (MCIStatus & MCI_TX_ACTIVE) {
		DataTxActiveCount++;
	}
	/* Rx active  */
	if (MCIStatus & MCI_RX_ACTIVE) {
		DataRxActiveCount++;
	}

	return;
}

/******************************************************************************
 ** Function name:	MCI_FIFOInterruptService
 **
 ** Descriptions:	Called by MCI interrupt handler when using FIFO
 **					interrupts and DMA is disabled
 **
 **
 ** parameters:			None
 ** Returned value:		None
 **
 ******************************************************************************/
// mthomas - static
static void MCI_FIFOInterruptService(void) {
#if !MCI_DMA_ENABLED
	DWORD MCIStatus;


	MCIStatus = MCI_STATUS;

	if ( MCIStatus & (FIFO_TX_INT_MASK ) )
	{
#if DEBUG_SDCARD_INTERRUPTS
		xprintf("--> FIFO_TX_INT_MASK\n");
#endif
		DataTxFIFOCount++; /* if using TX_HALF_EMPTY remove one WriteFifo below */
		if ( MCIStatus & MCI_TX_HALF_EMPTY ) /* empty is multiple of 512 block size */
		{

#if DEBUG_SDCARD_INTERRUPTS
		xprintf("----> More specifically: MCI_TX_HALF_EMPTY\n");
#endif


#if DEBUG_SDCARD_INTERRUPTS
		xprintf("\nTrying to write with MCI_WriteFIFO assembly function:\n"
				"\tWriteBlock[%d] = 0x%02x\n"
				"\t&WriteBlock[%d] = 0x%08x\n\n", TXBlockCounter, WriteBlock[TXBlockCounter], TXBlockCounter, &WriteBlock[TXBlockCounter]);

		//xprintf("For reference, SRC_REG[0] = %02x (address %08x); SRC_REG[1] = %02x (address %08x)\n", SRC_REG[0], &SRC_REG[0], SRC_REG[1], &SRC_REG[1]);
#endif

#if DEBUG_SDCARD_SALT
		xprintf("MCI_Status before WriteFifo: %08x; MCI_Fifo_Cnt: %08x;\n", MCI_STATUS, MCI_FIFO_CNT);
#endif
			// mthomas
			//MCI_WriteFifo((DWORD *)&WriteBlock[TXBlockCounter]); /* write 8 words to fifo */
			MCI_WriteFifo((volatile DWORD *)&WriteBlock[TXBlockCounter]); /* write 8 words to fifo */


#if 0
		{
				DWORD i;
				for(i=0; i<8; i++) {
					MCI_FIFO = WriteBlock[TXBlockCounter];
					TXBlockCounter+= 4;
				}
			}
#endif

#if DEBUG_SDCARD_SALT
			xprintf("MCI_Status after WriteFifo: %08x; MCI_Fifo_Cnt: %08x;\n", MCI_STATUS, MCI_FIFO_CNT);
#endif
			TXBlockCounter += 32;

#if DEBUG_SDCARD_INTERRUPTS
			xprintf("TXBlockCounter after MCI_WriteFifo = %d (BLOCK_LENGTH = %d)\n", TXBlockCounter, BLOCK_LENGTH);
#endif
		}
		if (TXBlockCounter == BLOCK_LENGTH) /* block complete */
		{
			TXBlockCounter = 0;
			MCI_MASK0 &= ~(FIFO_TX_INT_MASK); /* disable FIFO int until next block write */
			MCI_MASK1 &= ~(FIFO_TX_INT_MASK);
			/* wait for SD card to complete sending data i.e MCI_DATA_BLK_END interrupt */
		}
	}
	else if ( MCIStatus & (FIFO_RX_INT_MASK) )
	{
#if DEBUG_SDCARD_INTERRUPTS
		xprintf("--> More specifically: FIFO_RX_INT_MASK\n");
#endif
		DataRxFIFOCount++;
		if ( MCIStatus & MCI_RX_HALF_FULL ) /* if using RX_HALF_FULL remove one ReadFIFO below */
		{
#if DEBUG_SDCARD_INTERRUPTS
		xprintf("----> More specifically: MCI_RX_HALF_FULL\n");
#endif
			// mhomas
			// MCI_ReadFifo((DWORD *)&ReadBlock[RXBlockCounter]); /* read 8 words from fifo */
			MCI_ReadFifo((volatile DWORD *)&ReadBlock[RXBlockCounter]); /* read 8 words from fifo */
			RXBlockCounter += 32;
		}
		if (RXBlockCounter == BLOCK_LENGTH) /* block complete */
		{
			RXBlockCounter = 0;
		}
		// mthomas: maybe redundant
		if ( SDStatRead && RXBlockCounter == SD_BLOCK_LENGTH ) /* SD_STATUS complete */
		{
			SDStatRead = 0;
			RXBlockCounter = 0;
		}
		// mthomas end
	}
#endif

	DataFIFOCount++;
	return;
}

/******************************************************************************
 ** Function name:		MCI_IRQHandler
 **
 ** Descriptions:		MCI interrupt handler
 **						The handler to handle the block data write and read
 **						not for the commands.
 **
 ** parameters:			None
 ** Returned value:		None
 **
 ******************************************************************************/
void MCI_IRQHandler(void)
{

	IRQHandlerCounter++;

#if DEBUG_SDCARD_INTERRUPTS
	xprintf("*** Hit MCI_IRQHandler ***\n");
#endif
	DWORD MCI_Status;

	MCI_Status = MCI_STATUS;

#if DEBUG_SDCARD_INTERRUPTS
	xprintf("Interrupt MCI status: ");
#endif

	/* handle MCI_STATUS interrupt */
	if ( MCI_Status & DATA_ERR_INT_MASK )
	{

#if DEBUG_SDCARD_INTERRUPT_ERRORS
		xprintf("DATA_ERR_INT_MASK\n");
#endif

		MCI_DataErrorProcess();
		MCI_DataErrorProcess_count++;
		VICVectAddr = 0; /* Acknowledge Interrupt */
		return;
	}
	if ( MCI_Status & DATA_END_INT_MASK )
	{
#if DEBUG_SDCARD_INTERRUPTS
		xprintf("DATA_END_INT_MASK\n");
#endif
		MCI_DATA_END_InterruptService();
		MCI_DATA_END_InterruptService_count++;
		VICVectAddr = 0; /* Acknowledge Interrupt */
		return;
	}
	else if ( MCI_Status & FIFO_INT_MASK )
	{
#if DEBUG_SDCARD_INTERRUPTS
		xprintf("FIFO_INT_MASK\n");
#endif
		MCI_FIFOInterruptService();
		MCI_FIFOInterruptService_count++;
		VICVectAddr = 0; /* Acknowledge Interrupt */
		return;
	}
	else if ( MCI_Status & CMD_INT_MASK )
	{
#if DEBUG_SDCARD_INTERRUPTS
		xprintf("CMD_INT_MASK\n");
#endif
		MCI_CmdProcess();
		MCI_CmdProcess_count++;
		VICVectAddr = 0; /* Acknowledge Interrupt */
		return;
	}

	VICVectAddr = 0; /* Acknowledge Interrupt */
	return;
}

/******************************************************************************
** Function name:		MCI_GetCmdResp
**
** Descriptions:		Get response from the card. This module is always used
**						in pair with MCI_SendCmd()
**
** parameters:			Expected cmd data, expect response flag, pointer to the
**						response
**						Expected cmd data should be the same as that in SendCmd()
**						expect response flag could be	EXPECT_NO_RESP
**														EXPECT_SHORT_RESP
**														EXPECT_LONG_RESP
**						if GetCmdResp() is 0, check the pointer to the response
**						field to get the response value, if GetCmdResp() returns
**						non-zero, no need to check the response field, just resend
**						command or bailout.
** Returned value:		Response status, 0 is valid response.
**
******************************************************************************/
DWORD MCI_GetCmdResp( DWORD ExpectCmdData, DWORD ExpectResp, DWORD *CmdResp )
{
  DWORD CmdRespStatus = 0;
  DWORD LastCmdIndex;

  if ( ExpectResp == EXPECT_NO_RESP )
  {
	return ( 0 );
  }


  while ( 1 )
  {
	CmdRespStatus = MCI_STATUS;
	if ( CmdRespStatus & (MCI_CMD_TIMEOUT) )
	{
	  MCI_CLEAR = CmdRespStatus | MCI_CMD_TIMEOUT;
	  MCI_COMMAND = 0;
	  MCI_ARGUMENT = 0xFFFFFFFF;
	  return ( CmdRespStatus );
	}
	if (  CmdRespStatus & MCI_CMD_CRC_FAIL )
	{
	  MCI_CLEAR = CmdRespStatus | MCI_CMD_CRC_FAIL;
	  LastCmdIndex = MCI_COMMAND & 0x003F;
	  if ( (LastCmdIndex == SEND_OP_COND) || (LastCmdIndex == SEND_APP_OP_COND)
			|| (LastCmdIndex == STOP_TRANSMISSION) )
	  {
		MCI_COMMAND = 0;
		MCI_ARGUMENT = 0xFFFFFFFF;
		break;			/* ignore CRC error if it's a resp for SEND_OP_COND
						or STOP_TRANSMISSION. */
	  }
	  else
	  {
		#if DEBUG_SDCARD
		xprintf("*** CRCfail");
		#endif
		return ( CmdRespStatus );
	  }
	}
	else if ( CmdRespStatus & MCI_CMD_RESP_END )
	{
	  MCI_CLEAR = CmdRespStatus | MCI_CMD_RESP_END;
	  break;	/* cmd response is received, expecting response */
	}
  }

  if ( (MCI_RESP_CMD & 0x3F) != ExpectCmdData )
  {
	/* If the response is not R1, in the response field, the Expected Cmd data
	won't be the same as the CMD data in SendCmd(). Below four cmds have
	R2 or R3 response. We don't need to check if MCI_RESP_CMD is the same
	as the Expected or not. */
	if ( (ExpectCmdData != SEND_OP_COND) && (ExpectCmdData != SEND_APP_OP_COND)
		&& (ExpectCmdData != ALL_SEND_CID) && (ExpectCmdData != SEND_CSD) )
	{
	  CmdRespStatus = INVALID_RESPONSE;	/* Reuse error status */
	  return ( INVALID_RESPONSE );
	}
  }

  if ( ExpectResp == EXPECT_SHORT_RESP )
  {
	*CmdResp = MCI_RESP0;
  }
  else if ( ExpectResp == EXPECT_LONG_RESP )
  {
	*CmdResp = MCI_RESP0;
	*(CmdResp+1) = MCI_RESP1;
	*(CmdResp+2) = MCI_RESP2;
	*(CmdResp+3) = MCI_RESP3;
  }

  return ( 0 );	/* Read MCI_RESP0 register assuming it's not long response. */
}

/******************************************************************************
 ** Function name:		MCI_Send_IF_Cond
 **
 ** Descriptions:		CMD8 to detect SD-Card >= Version 2.0
 **
 ** parameters:			None
 ** Returned value:		true or false, true if card has response back before
 **						timeout, false is timeout on the command.
 **
 ** added by Martin Thomas with information from Juri Haberland, Chan and
 ** the SanDisk Product Manuals V2.2
 ******************************************************************************/
// mthomas - static
DWORD MCI_Send_IF_Cond(void) {
	volatile DWORD i; // mthomas - volatile
	DWORD retryCount;
	DWORD respStatus;
	DWORD respValue[4];

	retryCount = 20; /* reset retry counter */
	while (retryCount > 0) {
		MCI_POWER &= ~(1 << 6 );
		/* Clear Open Drain output control for SD */
		for (i = 0; i < 0x3000; i++) {
			;
		}

		/* Send CMD8 command repeatedly until the response is back correctly or timeout */
		MCI_SendCmd( SEND_IF_COND, SEND_IF_COND_ARG , EXPECT_SHORT_RESP, 0);
		respStatus = MCI_GetCmdResp( SEND_IF_COND, EXPECT_SHORT_RESP, (DWORD *)&respValue[0]);

		/* mthomas TODO: check result bits - for now just check if no timeout */

#if DEBUG_SDCARD
		xprintf("SEND_IF_COND response: ");
		uprintfx_dword(respValue[0]);
		xprintf("\n");
#endif

		if ( !(respStatus & MCI_CMD_TIMEOUT)) {
			return ( TRUE ); /* response is back and correct. */
		}
		for (i = 0; i < 0x20; i++)
			;
		retryCount--;
	}
	return ( FALSE );
}

/******************************************************************************
 ** Function name:		MCI_Send_ACMD
 **
 ** Descriptions:		CMD55, before sending an ACMD, call this routine first
 **
 ** parameters:			None
 ** Returned value:		true or false, true if card has responded before timeout.
 **						false is timeout.
 **
 ******************************************************************************/
// mthomas - static
DWORD MCI_Send_ACMD(void) {
	volatile DWORD i; // mthomas - volatile
	DWORD retryCount;
	DWORD CmdArgument;
	DWORD respStatus;
	DWORD respValue[4];

	// mthomas - warning: MCI_CardType may be set from
	// extern to other than UNKOWN and this
	// will fail on init (workaround: added MCI_CardType = UNKNOWN below)
	// laebi: add CardType SDHC_CARD, otherwise it wouldnt work
	if (MCI_CardType == SD_CARD || MCI_CardType == SDHC_CARD) {
		CmdArgument = CardRCA; /* Use the address from SET_RELATIVE_ADDR cmd */
	} else /* if MMC or unknown card type, use 0x0. */
	{
		CmdArgument = 0x00000000;
	}

	retryCount = 20;
	while (retryCount > 0) {
		/* Send CMD55 command followed by an ACMD */
		MCI_SendCmd( APP_CMD, CmdArgument, EXPECT_SHORT_RESP, 0);
		respStatus = MCI_GetCmdResp( APP_CMD, EXPECT_SHORT_RESP, (DWORD *)&respValue[0]);
		if ( !respStatus && (respValue[0] & CARD_STATUS_ACMD_ENABLE)) /* Check if APP_CMD enabled */
		{
			return ( TRUE );
		}
		for (i = 0; i < 0x20; i++) {
			;
		}
		retryCount--;
	}
	return ( FALSE );
}

/******************************************************************************
** Function name:		MCI_Go_Idle_State
**
** Descriptions:		CMD0, the very first command to be sent to initialize
**						either MMC or SD card.
**
** parameters:			None
** Returned value:		true or false, true if card has been initialized.
**
******************************************************************************/
DWORD MCI_Go_Idle_State( void )
{
  DWORD retryCount;
  DWORD respStatus;
  DWORD respValue[4];

  retryCount = 0x20;
  while ( retryCount > 0 )
  {
	/* Send CMD0 command repeatedly until the response is back correctly */
    MCI_SendCmd( GO_IDLE_STATE, 0x00000000, EXPECT_NO_RESP, 0 );
	respStatus = MCI_GetCmdResp( GO_IDLE_STATE, EXPECT_NO_RESP, (DWORD *)respValue );
	if ( respStatus == 0 )
	{
	  break;
	}
	retryCount--;
  }

  if ( respStatus != 0 )		/* timeout, give up */
  {
	return ( FALSE );
  }
  return( TRUE );
}


/******************************************************************************
 ** Function name:		MCI_Send_ACMD_OP_Cond
 **
 ** Descriptions:		If Send_OP_Cond is timeout, it's not a MMC card, try
 **						this combination to see if we can communicate with
 **						a SD card.
 **
 ** parameters:			ACMD41 argument (mthomas)
 ** Returned value:		true or false, true if card has been initialized.
 **						respValue[0] in resp0
 **
 ** modified by Martin Thomas (in: arg, out resp0)
 ******************************************************************************/
// mthomas - static
DWORD MCI_Send_ACMD_OP_Cond(DWORD acmd41_arg, DWORD *resp0) {

	volatile DWORD i; // mthomas - volatile
	DWORD retryCount;
	DWORD respStatus;
	DWORD respValue[4];

	respStatus = -1; /* to avoid warning */

	/* timeout on SEND_OP_COND command on MMC, now, try SEND_APP_OP_COND
	 command to SD */
	// mthomas - less retries retryCount = 0x200;			/* reset retry counter */
	retryCount = 0xA0;

	while (retryCount > 0) {
		MCI_POWER &= ~(1 << 6 );
		/* Clear Open Drain output control for SD */
		for (i = 0; i < 0x3000; i++) {
			;
		}

		if (MCI_Send_ACMD() == FALSE) {
			retryCount--; // mthomas - line added
			continue;
		}

		/* Send ACMD41 command repeatedly until the response is back correctly */
		//MCI_SendCmd( SEND_APP_OP_COND, OCR_INDEX, EXPECT_SHORT_RESP, 0 );
		MCI_SendCmd( SEND_APP_OP_COND, acmd41_arg, EXPECT_SHORT_RESP, 0);
		respStatus = MCI_GetCmdResp( SEND_APP_OP_COND, EXPECT_SHORT_RESP, (DWORD *)&respValue[0]);


		/* OCR is supposed to be returned when ACMD41 is sent.
		 * 		[14:0] 	reserved.
		 * 		[23:15] should be 1 (because that shows acceptable voltage levels.
		 * 		[24]	switching to 1.8V accepted (we don't need this)
		 * 		[29:25] reserved
		 * 		[30]	CCS (Card capacity status... only valid when [31] is set)
		 * 		[31]	Card power up status bit (busy)
		 *
		 * 		At MCI_PCLK of 32MHz, we get 0x00FF8000 which is all perfect except CCS
		 * 		not being set (because card power up status bit is 0.)
		 *
		 * 		mpaer: I don't know why the original below code checks for respValue[0] & 0x80000000
		 * 		because R3 (OCR) is never in that format. I will modify it to be in this format.
		 *
		 * 		Since we are unsure of CCS and Card powerup status bit, we will accept any value there.
		 * 		So respValue[0] can be:
		 *
		 * 			* 0x00FF8000
		 * 			* 0x40FF8000
		 * 			* 0x80FF8000
		 * 			* 0xC0FF8000
		 *
		 *		Edit: I realize now that respValue[0] & 0x80000000 will be true if and only if bit 31
		 *		which is (card ready) is set. That's what it's checking for.
		 *
		 * 		My try:
		 * 		if ( !(respStatus & MCI_CMD_TIMEOUT) && ( (respValue[0] & 0x00FF8000) || (respValue[0] & 0x80FF8000) || (respValue[0] & 0xC0FF8000) || (respValue[0] & 0x40FF8000) )) {
		 */

		if ( !(respStatus & MCI_CMD_TIMEOUT) && (respValue[0] & 0x80000000)) {
			if (resp0 != NULL) {
				*resp0 = respValue[0];
			}
			return ( TRUE ); /* response is back and correct. */
		}
		for (i = 0; i < 0x20; i++) {
			;
		}
		retryCount--;
	}


#if DEBUG_SDCARD
		 xprintf("Last MCI_SEND_ACMD_OP_COND response: ");
		uprintfx_dword(respValue[0]);
		xprintf("\t(respStatus = ");
		uprintfi((int) respStatus);
		xprintf(")\n");
#endif
	return ( FALSE );
}


/******************************************************************************
** Function name:		MCI_Send_OP_Cond
**
** Descriptions:		CMD1 for MMC
**
** parameters:			None
** Returned value:		true or false, true if card has response back before
**						timeout, false is timeout on the command.
**
******************************************************************************/
DWORD MCI_Send_OP_Cond( void )
{
  DWORD i, retryCount;
  DWORD respStatus;
  DWORD respValue[4];

  retryCount = 0x200;			/* reset retry counter */
  while ( retryCount > 0 )
  {
	/* Send CMD1 command repeatedly until the response is back correctly */
	MCI_SendCmd( SEND_OP_COND, OCR_INDEX, EXPECT_SHORT_RESP, 0 );
	respStatus = MCI_GetCmdResp( SEND_OP_COND, EXPECT_SHORT_RESP, (DWORD *)&respValue[0] );
	/* bit 0 and bit 2 must be zero, or it's timeout or CRC error */
	if ( !(respStatus & MCI_CMD_TIMEOUT) && (respValue[0] & 0x80000000) )
	{
	  return ( TRUE );	/* response is back and correct. */
	}
	for ( i = 0; i < 0x20; i++ );
	retryCount--;
  }
  return( FALSE );
}

/******************************************************************************
** Function name:		MCI_SendCmd
**
** Descriptions:		The routine is used to send a CMD to the card
**
** parameters:			CmdIndex, Argument, ExpectResp Flag, AllowTimeout flag
** Returned value:		None
**
******************************************************************************/
void MCI_SendCmd( DWORD CmdIndex, DWORD Argument, DWORD ExpectResp, DWORD AllowTimeout )
{
  DWORD i, CmdData = 0;
  DWORD CmdStatus;

  /* the command engine must be disabled when we modify the argument
  or the peripheral resends */
  while ( (CmdStatus = MCI_STATUS) & MCI_CMD_ACTIVE )	/* Command in progress. */
  {
	MCI_COMMAND = 0;
	MCI_CLEAR = CmdStatus | MCI_CMD_ACTIVE;
  }
  for ( i = 0; i < 0x100; i++ );

  /*set the command details, the CmdIndex should 0 through 0x3F only */
  CmdData |= (CmdIndex & 0x3F);	/* bit 0 through 5 only */
  if ( ExpectResp == EXPECT_NO_RESP )			/* no response */
  {
	CmdData &= ~((1 << 6) | (1 << 7));		/* Clear long response bit as well */
  }
  else if ( ExpectResp == EXPECT_SHORT_RESP )	/* expect short response */
  {
	CmdData |= (1 << 6);
  }
  else if ( ExpectResp == EXPECT_LONG_RESP )	/* expect long response */
  {
	CmdData |= (1 << 6) | (1 << 7);
  }

  if ( AllowTimeout )			/* allow timeout or not */
  {
	CmdData |= (1 << 8);
  }
  else
  {
	CmdData &= ~(1 << 8);
  }

  /*send the command*/
  CmdData |= (1 << 10);		/* This bit needs to be set last. */
  MCI_ARGUMENT = Argument;	/* Set the argument first, finally command */
  MCI_COMMAND = CmdData;
  return;
}

/******************************************************************************
 ** Function name:		MCI_Set_MCIClock
 **
 ** Descriptions:		Set MCI clock rate, during initialization phase < 400K
 **						during data phase < 20Mhz.
 **
 ** parameters:			Clock rate to be set
 ** Returned value:		None
 **
 ******************************************************************************/
void MCI_Set_MCIClock(DWORD ClockRate) {
	volatile DWORD i; // mthomas volatile
	DWORD ClkValue = 0;

	if (ClockRate == SLOW_RATE) {
		ClkValue |= MCLKDIV_SLOW; /* slow clock */
	} else if (ClockRate == NORMAL_RATE) {
		ClkValue |= MCLKDIV_NORMAL;/* normal clock */
	} else {
		// mthomas - added default - low
		ClkValue |= MCLKDIV_SLOW;
	}

	MCI_CLOCK &= ~(0xFF);
	/* clear clock divider */
	// laebi: Disable MCI clock output when bus is idle (bit 9)
	// They have it set bit 9 (power save) to 1 here.

	// Power save
	//MCI_CLOCK |= (1 << 8) | (1 << 9) | ClkValue;

	// Wide bus
	MCI_CLOCK |= (1 << 8) | (1 << 11) | ClkValue;

	// [ no settings ]
	//MCI_CLOCK |= (1 << 8) | ClkValue;

	// Wide bus, power save
	//MCI_CLOCK |= (1 << 8) | (1 << 9) | (1 << 11) | ClkValue;

	for (i = 0; i < 0x100; i++)
		; /* delay 3MCLK + 2PCLK before next write */

	return;
}

/******************************************************************************
 ** Function name:		MCI_CardInit
 **
 ** Descriptions:		Try CMD1 first for MMC, if it's timeout, try CMD55
 **						and CMD41 for SD, if both failed, initialization faliure,
 **						bailout with unknown card type. Otherwise, return the
 **						card type, either MMC or SD.
 **
 ** parameters:			None
 ** Returned value:		Card type.
 **
 ** extended by Martin Thomas with information from Juri Haberland,
 ** public available Information for SDHC, Chan's fat-code and
 ** the SanDisk SD-Card manual V2.2
 ******************************************************************************/
DWORD MCI_CardInit(void) {
	volatile DWORD i;
	DWORD CardType= CARD_UNKNOWN;
	DWORD acmd41_arg;
	DWORD resp0;

	MCI_CardType = CARD_UNKNOWN;

	// Send CMD0
	if (MCI_Go_Idle_State() == FALSE) {
#if DEBUG_SDCARD
		xprintf("Init Go Idle failed\n");
#endif
		return ( CARD_UNKNOWN );
	}

	// Send CMD8
	if (MCI_Send_IF_Cond() == TRUE) {
#if DEBUG_SDCARD
		xprintf("no timeout on CMD8 -> SD-Card>=Version 2.0\n");
#endif
		CardType = SDV2_CARD;
		acmd41_arg = OCR_CARD_HIGH_CAPACITY | OCR_HIGH_VOLTAGE_RANGE;
	} else {
#if DEBUG_SDCARD
		xprintf("timeout CMD8 -> SD-Card<Version 2.0 or MMC\n");
#endif
		acmd41_arg = OCR_HIGH_VOLTAGE_RANGE;
	}

	if (MCI_Send_ACMD_OP_Cond(acmd41_arg, &resp0) == TRUE) {
#if DEBUG_SDCARD
		xprintf("ACMD41 success -> SD-Card SC or HC\n");

		xprintf("OCR status now: ");
		uprintfx_dword(resp0);
		xprintf("\n\n");
#endif

		if (resp0 & OCR_CARD_HIGH_CAPACITY) {
#if DEBUG_SDCARD
			xprintf("SDHC detected - EXPERIMENTAL!\n");
#endif



			CardType = SDHC_CARD;
		} else {
#if DEBUG_SDCARD
			xprintf("SDSC detected\n");
#endif
			CardType = SDSC_CARD;
		}
	} else {
#if DEBUG_SDCARD
		xprintf("MCI_Send_ACMD failed -> MMC, invalid power-range or no card\n");
#endif


		MCI_POWER |= (1 << 6 );
		/* Set Open Drain output control for MMC */
		for (i = 0; i < 0x3000; i++) {
			;
		}
		/* try CMD1 of MMC */
		if (MCI_Send_OP_Cond() == TRUE) {
			CardType = MMC_CARD;
		}
	}

	MCI_CardType = CardType;

	return (CardType );
}

/******************************************************************************
** Function name:		MCI_Check_CID
**
** Descriptions:		Send CMD2, ALL_SEND_CID
**
** parameters:			None
** Returned value:		If not timeout, return true.
**
******************************************************************************/
DWORD MCI_Check_CID( void )
{
  DWORD i, retryCount;
  DWORD respStatus;
  DWORD respValue[4];

#if DEBUG_SDCARD
  xprintf("In MCI_Check_CID...\n");
#endif

  /* This command is normally after CMD1(MMC) or ACMD41(SD). */
  retryCount = 0x20;			/* reset retry counter */
  while ( retryCount > 0 )
  {
	/* Send CMD2 command repeatedly until the response is back correctly */
	MCI_SendCmd( ALL_SEND_CID, 0, EXPECT_LONG_RESP, 0 );
	respStatus = MCI_GetCmdResp( ALL_SEND_CID, EXPECT_LONG_RESP, (DWORD *)&respValue[0] );
	/* bit 0 and bit 2 must be zero, or it's timeout or CRC error */

#if DEBUG_SDCARD
	xprintf("Checking MCI_Check_CID response: ");
	uprintfx_dword(*(respValue+3));
	xprintf(" ");
	uprintfx_dword(*(respValue+2));
	xprintf(" ");
	uprintfx_dword(*(respValue+1));
	xprintf(" ");
	uprintfx_dword(*(respValue+0));
	xprintf(" (respStatus = ");
	uprintfx_dword(respStatus);
	xprintf(")\n");
#endif

	if ( !(respStatus & MCI_CMD_TIMEOUT) )
	{
	  return ( TRUE );	/* response is back and correct. */
	}
	for ( i = 0; i < 0x20; i++ );
	retryCount--;
  }
  return ( FALSE );
}


/******************************************************************************
** Function name:		MCI_Set_Address
**
** Descriptions:		Send CMD3, STE_RELATIVE_ADDR, should after CMD2
**
** parameters:			None
** Returned value:		TRUE if response is back before timeout.
**
******************************************************************************/
DWORD MCI_Set_Address( void )
{
  DWORD i, retryCount;
  DWORD respStatus;
  DWORD respValue[4];
  DWORD CmdArgument;

  /* If it's a SD card, SET_RELATIVE_ADDR is to get the address
  from the card and use this value in RCA, if it's a MMC, set default
  RCA addr. 0x00010000. */
  if ( MCI_CardType == SD_CARD )
  {
	CmdArgument = 0;
  }
  else			/* If it's unknown or MMC_CARD, fix the RCA address */
  {
	CmdArgument = 0x00010000;
  }

  retryCount = 0x20;			/* reset retry counter */
  while ( retryCount > 0 )
  {
	/* Send CMD3 command repeatedly until the response is back correctly */
	MCI_SendCmd( SET_RELATIVE_ADDR, CmdArgument, EXPECT_SHORT_RESP, 0 );
	respStatus = MCI_GetCmdResp( SET_RELATIVE_ADDR, EXPECT_SHORT_RESP, (DWORD *)&respValue[0] );

#if DEBUG_SDCARD
	xprintf("Checking MCI_Set_Address response: ");
	uprintfx_dword(*(respValue+0));
	xprintf(" (respStatus = ");
	uprintfx_dword(respStatus);
	xprintf(")\n");
#endif

	/* bit 0 and bit 2 must be zero, or it's timeout or CRC error */
	/* It should go to IDEN state and bit 8 should be 1 */
	if ( !(respStatus & MCI_CMD_TIMEOUT) && ((respValue[0] & (0x0F << 8)) == 0x0500) )
	{
	  CardRCA = respValue[0] & 0xFFFF0000;	/* Save the RCA value from SD card */
	  return ( TRUE );	/* response is back and correct. */
	}
	for ( i = 0; i < 0x20; i++ );
	retryCount--;
  }
  return ( FALSE );
}


/******************************************************************************
** Function name:		MCI_Send_CSD
**
** Descriptions:		CMD9, SEND_CSD cmd, it should be sent only at
**						STBY state and after CMD3. See MMC and SD spec. state
**						diagram.
**
** parameters:			None
** Returned value:		Response value
**
******************************************************************************/
DWORD MCI_Send_CSD( void )
{
  DWORD i, retryCount;
  DWORD respStatus;
  DWORD respValue[4];
  DWORD CmdArgument;

  if ( MCI_CardType == SD_CARD )
  {
	CmdArgument = CardRCA;
  }
  else			/* if MMC or unknown card type, use default RCA addr. */
  {
	CmdArgument = 0x00010000;
  }

  retryCount = 0x20;
  while ( retryCount > 0 )
  {
	/* Send SET_BLOCK_LEN command before read and write */
	MCI_CLEAR |= (MCI_CMD_TIMEOUT | MCI_CMD_CRC_FAIL | MCI_CMD_RESP_END);
	MCI_SendCmd( SEND_CSD, CmdArgument, EXPECT_LONG_RESP, 0 );
	respStatus = MCI_GetCmdResp( SEND_CSD, EXPECT_LONG_RESP, (DWORD *)&respValue[0] );

#if DEBUG_SDCARD
	xprintf("Checking MCI_Send_CSD response: ");
	uprintfx_dword(*(respValue+3));
	xprintf(" ");
	uprintfx_dword(*(respValue+2));
	xprintf(" ");
	uprintfx_dword(*(respValue+1));
	xprintf(" ");
	uprintfx_dword(*(respValue+0));  /* MSNibble */
	xprintf(" (respStatus = ");
	uprintfx_dword(respStatus);
	xprintf(")\n");

	xprintf("\n\n---- Card information ----\n");
	xprintf("CSD Structure (Type of SD):\t");
		((respStatus & 0xC0000000) == 0) ? xprintf("SD Standard") : xprintf("SDHC");


#endif

	if ( !respStatus )
	{
	  return ( TRUE );
	}
	for ( i = 0; i < 0x20; i++ );
	retryCount--;
  }
  return ( FALSE );
}

/******************************************************************************
** Function name:		MCI_Select_Card
**
** Descriptions:		CMD7, SELECT_CARD, should be after CMD9, the state
**						will be inter-changed between STBY and TRANS after
**						this cmd.
**
** parameters:			None
** Returned value:		return false if response times out.
**
******************************************************************************/
DWORD MCI_Select_Card( void )
{
  DWORD i, retryCount;
  DWORD respStatus;
  DWORD respValue[4];
  DWORD CmdArgument;

  if ( MCI_CardType == SD_CARD )
  {
	CmdArgument = CardRCA;
  }
  else			/* if MMC or unknown card type, use default RCA addr. */
  {
	CmdArgument = 0x00010000;
  }

  retryCount = 0x20;
  while ( retryCount > 0 )
  {
	/* Send SELECT_CARD command before read and write */
	MCI_CLEAR |= (MCI_CMD_TIMEOUT | MCI_CMD_CRC_FAIL | MCI_CMD_RESP_END);
	MCI_SendCmd( SELECT_CARD, CmdArgument, EXPECT_SHORT_RESP, 0 );
	respStatus = MCI_GetCmdResp( SELECT_CARD, EXPECT_SHORT_RESP, (DWORD *)&respValue[0] );

#if DEBUG_SDCARD
	xprintf("Checking MCI_Select_Card response: ");
	uprintfx_dword(*(respValue+0));
	xprintf(" (respStatus = ");
	uprintfx_dword(respStatus);
	xprintf(")\n");
#endif


	if ( !respStatus && ((respValue[0] & (0x0F << 8)) == 0x0700 ))
	{						/* Should be in STANDBY state now and ready */
	  return ( TRUE );
	}
	for ( i = 0; i < 0x20; i++ );
	retryCount--;
  }
  return ( FALSE );
}

/******************************************************************************
** Function name:		MCI_Send_Status
**
** Descriptions:		CMD13, SEND_STATUS, the most important cmd to
**						debug the state machine of the card.
**
** parameters:			None
** Returned value:		Response value(card status), true if the ready bit
**						is set in the card status register, if timeout, return
**						INVALID_RESPONSE 0xFFFFFFFF.
**
******************************************************************************/
DWORD MCI_Send_Status( void )
{
  DWORD retryCount;
  DWORD respStatus;
  DWORD respValue[4];
  DWORD CmdArgument;

  if ( MCI_CardType == SD_CARD )
  {
	CmdArgument = CardRCA;
  }
  else			/* if MMC or unknown card type, use default RCA addr. */
  {
	CmdArgument = 0x00010000;
  }

  /* Note that, since it's called after the block write and read, this timeout
  is important based on the clock you set for the data communication. */
  retryCount = 0x2000;
  while ( retryCount > 0 )
  {
	/* Send SELECT_CARD command before read and write */
	MCI_CLEAR |= (MCI_CMD_TIMEOUT | MCI_CMD_CRC_FAIL | MCI_CMD_RESP_END);
	MCI_SendCmd( SEND_STATUS, CmdArgument, EXPECT_SHORT_RESP, 0 );
	respStatus = MCI_GetCmdResp( SEND_STATUS, EXPECT_SHORT_RESP, (DWORD *)&respValue[0] );


#if 0
	xprintf("Checking MCI_Send_Status response: ");
	uprintfx_dword(*(respValue+0));
	xprintf(" (respStatus = ");
	uprintfx_dword(respStatus);
	xprintf(")\n");
#endif

	if ( !respStatus && (respValue[0] & (1 << 8)) )
	{ /* The ready bit should be set, it should be in either TRAN or RCV state now */
	  return ( respValue[0] );
	}
	retryCount--;
  }
  return ( INVALID_RESPONSE );
}

/******************************************************************************
** Function name:		MCI_Set_BlockLen
**
** Descriptions:		CMD16, SET_BLOCKLEN, called after CMD7(SELECT_CARD)
**						called in the TRANS state.
**
** parameters:			The length of the data block to be written or read.
** Returned value:		true or false, return TRUE if ready bit is set, and it's
**						in TRANS state.
**
******************************************************************************/
DWORD MCI_Set_BlockLen( DWORD blockLength )
{
  DWORD i, retryCount;
  DWORD respStatus;
  DWORD respValue[4];

  retryCount = 0x20;
  while ( retryCount > 0 )
  {
	/* Send SET_BLOCK_LEN command before read and write */
	MCI_CLEAR |= (MCI_CMD_TIMEOUT | MCI_CMD_CRC_FAIL | MCI_CMD_RESP_END);
	MCI_SendCmd( SET_BLOCK_LEN, blockLength, EXPECT_SHORT_RESP, 0 );
	respStatus = MCI_GetCmdResp( SET_BLOCK_LEN, EXPECT_SHORT_RESP, (DWORD *)&respValue[0] );

#if DEBUG_SDCARD
	xprintf("Checking MCI_Set_BlockLen response: ");
	uprintfx_dword(*(respValue+0));
	xprintf(" (respStatus = ");
	uprintfx_dword(respStatus);
	xprintf(")\n");
#endif


	/* bit 9 through 12 should be in transfer state now. bit 8 is ready. */
	if ( !respStatus && ((respValue[0] & (0x0F << 8)) == 0x0900))
	{
	  return ( TRUE );
	}
	for ( i = 0; i < 0x20; i++ );
	retryCount--;
  }
  return ( FALSE );
}

/******************************************************************************
** Function name:		MCI_Send_ACMD_Bus_Width
**
** Descriptions:		ACMD6, SET_BUS_WIDTH, if it's SD card, we can
**						use the 4-bit bus instead of 1-bit. This cmd
**						can only be called during TRANS state.
**						Since it's a ACMD, CMD55 APP_CMD needs to be
**						sent out first.
**
** parameters:			Bus width value, 1-bit is 0, 4-bit is 10
** Returned value:		true or false, true if the card is still in the
**						TRANS state after the cmd.
**
******************************************************************************/
DWORD MCI_Send_ACMD_Bus_Width( DWORD buswidth )
{
  DWORD i, retryCount;
  DWORD respStatus;
  DWORD respValue[4];

  retryCount = 0x20;			/* reset retry counter */
  while ( retryCount > 0 )
  {
	if ( MCI_Send_ACMD() == FALSE )
	{
	  continue;
	}
	/* Send ACMD6 command to set the bus width */
	MCI_SendCmd( SET_ACMD_BUS_WIDTH, buswidth, EXPECT_SHORT_RESP, 0 );
	respStatus = MCI_GetCmdResp( SET_ACMD_BUS_WIDTH, EXPECT_SHORT_RESP, (DWORD *)&respValue[0] );


#if DEBUG_SDCARD
	xprintf("Checking MCI_Send_ACMD_Bus_Width response: ");
	uprintfx_dword(*(respValue+0));
	xprintf(" (respStatus = ");
	uprintfx_dword(respStatus);
	xprintf(")\n");
#endif


	if ( !respStatus && ((respValue[0] & (0x0F << 8)) == 0x0900) )
	{
	  return ( TRUE );	/* response is back and correct. */
	}
	for ( i = 0; i < 0x20; i++ );
	retryCount--;
  }
  return( FALSE );
}


/******************************************************************************
** Function name:		SD_Set_BusWidth
**
** Descriptions:		1-bit bus or 4-bit bus.
**
** parameters:			bus width
** Returned value:		TRUE or FALSE
**
******************************************************************************/
DWORD SD_Set_BusWidth( DWORD width )
{
  DWORD i;

  for ( i = 0; i < 0x10; i++ );	/* delay 3MCLK + 2PCLK  */
  if ( width == SD_1_BIT )
  {
	MCI_CLOCK &=  ~(1 << 11);	/* 1 bit bus */
  }
  else if ( width == SD_4_BIT )
  {
	MCI_CLOCK |=  (1 << 11);/* 4 bit bus */
  }

  if ( MCI_Send_ACMD_Bus_Width( width == SD_1_BIT ? BUS_WIDTH_1BIT : BUS_WIDTH_4BITS ) == FALSE )
  {
	return( FALSE );
  }
  return TRUE;
}


/******************************************************************************
 ** Function name:		MCI_CheckStatus
 **
 ** Descriptions:		MCI Check status before and after the block read and
 **						write. Right after the block read and write, this routine
 **						is important that, even the FIFO is empty, complete
 **						block has been sent, but, data is still being written
 **						to the card, this routine is to ensure that the data
 **						has been written based on the state of the card, not
 **						by the length being set.
 **
 ** parameters:			None
 ** Returned value:		TRUE or FALSE
 **
 ******************************************************************************/
DWORD MCI_CheckStatus(void) {
	DWORD respValue;

	while ( 1) // mthomas TODO: possible deadlock
	{
		if ( (respValue = MCI_Send_Status()) == INVALID_RESPONSE) {
			break;
		} else {
			/* The only valid state is TRANS per MMC and SD state diagram.
			 RCV state may be seen, but, I have found that it happens
			 only when TX_ACTIVE or RX_ACTIVE occurs before the WRITE_BLOCK and
			 READ_BLOCK cmds are being sent, which is not a valid sequence. */
			if ( (respValue & (0x0F << 8)) == 0x0900) {
				return ( TRUE );
			}
		}
	}

	return ( FALSE );
}


/******************************************************************************
 ** Function name:		MCI_Send_Stop
 **
 ** Descriptions:		CMD12, STOP_TRANSMISSION. if that happens, the card is
 **						maybe in a unknown state that need a warm reset.
 **
 ** parameters:			None
 ** Returned value:		true or false, true if, at least, the card status
 **						shows ready bit is set.
 **
 ******************************************************************************/
DWORD MCI_Send_Stop(void) {
	volatile DWORD i; // mthomas - volatile
	DWORD retryCount;
	DWORD respStatus;
	DWORD respValue[4];

	retryCount = 0x20;
	while (retryCount > 0) {
		MCI_CLEAR = 0x7FF;
		MCI_SendCmd( STOP_TRANSMISSION, 0x00000000, EXPECT_SHORT_RESP, 0);
		respStatus = MCI_GetCmdResp( STOP_TRANSMISSION, EXPECT_SHORT_RESP, (DWORD *)respValue);
		/* ready bit, bit 8, should be set in the card status register */
		if ( !respStatus && (respValue[0] & (1 << 8))) {
			return ( TRUE );
		}
		for (i = 0; i < 0x20; i++) {
			;
		}
		retryCount--;
	}
	return ( FALSE );
}


/******************************************************************************
 ** Function name:		MCI_Send_Write_Block
 **
 ** Descriptions:		CMD24, WRITE_BLOCK, send this cmd in the TRANS state
 **						to write a block of data to the card.
 **
 ** parameters:			block number
 ** Returned value:		Response value
 **
 ** modified by mthomas
 ******************************************************************************/
// mthomas - static
static DWORD MCI_Send_Write_Block(DWORD blockNum) {
	volatile DWORD i; // mthomas - volatile
	DWORD retryCount;
	DWORD respStatus;
	DWORD respValue[4];

	retryCount = 0x20;
	while (retryCount > 0) {
		MCI_CLEAR = 0x7FF;
#if 1
		// mthomas
		if (MCI_CardType == SDHC_CARD) {
			MCI_SendCmd( WRITE_BLOCK, blockNum, EXPECT_SHORT_RESP, 0);
		} else {
			MCI_SendCmd( WRITE_BLOCK, blockNum * BLOCK_LENGTH, EXPECT_SHORT_RESP, 0);
		}
#else
		// NXP original
		MCI_SendCmd( WRITE_BLOCK, blockNum * BLOCK_LENGTH, EXPECT_SHORT_RESP, 0 );
#endif
		respStatus = MCI_GetCmdResp( WRITE_BLOCK, EXPECT_SHORT_RESP, (DWORD *)&respValue[0]);
		/* it should be in the transfer state, bit 9~12 is 0x0100 and bit 8 is 1 */
		if ( !respStatus && ((respValue[0] & (0x0F << 8)) == 0x0900)) {
			return ( TRUE ); /* ready and in TRAN state */
		}

		for (i = 0; i < 0x20; i++) {
			;
		}
		retryCount--;
	}
	return ( FALSE ); /* Fatal error */
}

/******************************************************************************
 ** Function name:		MCI_Send_Write_Multiple_Block
 **
 ** Descriptions:		CMD25, WRITE_MULTIPLE_BLOCK , send this cmd in the
 **						TRANS state to write several blocks of data to the card.
 **
 ** parameters:			block number
 ** Returned value:		Response value
 **
 ** Added by Lorenz Aebi
 ******************************************************************************/
// mthomas - static
static DWORD MCI_Send_Write_Multiple_Block(DWORD blockNum) {
	volatile DWORD i; // mthomas - volatile
	DWORD retryCount;
	DWORD respStatus;
	DWORD respValue[4];

	retryCount = 0x20;
	while (retryCount > 0) {
		MCI_CLEAR = 0x7FF;
#if 1
		// mthomas
		if (MCI_CardType == SDHC_CARD) {
			MCI_SendCmd( WRITE_MULTIPLE_BLOCK, blockNum, EXPECT_SHORT_RESP, 0);
		} else {
			MCI_SendCmd( WRITE_MULTIPLE_BLOCK, blockNum * BLOCK_LENGTH, EXPECT_SHORT_RESP, 0);
		}
#else
		// NXP original
		MCI_SendCmd( WRITE_MULTIPLE_BLOCK, blockNum * BLOCK_LENGTH, EXPECT_SHORT_RESP, 0 );
#endif
		respStatus = MCI_GetCmdResp( WRITE_MULTIPLE_BLOCK, EXPECT_SHORT_RESP, (DWORD *)&respValue[0]);
		/* it should be in the transfer state, bit 9~12 is 0x0100 and bit 8 is 1 */
		if ( !respStatus && ((respValue[0] & (0x0F << 8)) == 0x0900)) {
			return ( TRUE ); /* ready and in TRAN state */
		}

		for (i = 0; i < 0x20; i++) {
			;
		}
		retryCount--;
	}
	return ( FALSE ); /* Fatal error */
}

/******************************************************************************
 ** Function name:		MCI_Send_Read_Block
 **
 ** Descriptions:		CMD17, READ_SINGLE_BLOCK, send this cmd in the TRANS
 **						state to read a block of data from the card.
 **
 ** parameters:			block number
 ** Returned value:		Response value
 **
 ** modified by mthomas
 ******************************************************************************/
// mthomas - static
static DWORD MCI_Send_Read_Block(DWORD blockNum) {
	volatile DWORD i; // mthomas - volatile
	DWORD retryCount;
	DWORD respStatus;
	DWORD respValue[4];

	retryCount = 0x20;
	while (retryCount > 0) {
		MCI_CLEAR = 0x7FF;
#if 1
		// mthomas
		if (MCI_CardType == SDHC_CARD) {
			MCI_SendCmd( READ_SINGLE_BLOCK, blockNum, EXPECT_SHORT_RESP, 0);
		} else {
			MCI_SendCmd( READ_SINGLE_BLOCK, blockNum * BLOCK_LENGTH, EXPECT_SHORT_RESP, 0);
		}
#else
		// NXP original
		MCI_SendCmd( READ_SINGLE_BLOCK, blockNum * BLOCK_LENGTH, EXPECT_SHORT_RESP, 0 );
#endif
		respStatus = MCI_GetCmdResp( READ_SINGLE_BLOCK, EXPECT_SHORT_RESP, (DWORD *)&respValue[0]);
		/* it should be in the transfer state, bit 9~12 is 0x0100 and bit 8 is 1 */
		if ( !respStatus && ((respValue[0] & (0x0F << 8)) == 0x0900)) {
			return ( TRUE ); /* ready and in TRAN state */
		}
		for (i = 0; i < 0x20; i++) {
			;
		}
		retryCount--;
	}
	return ( FALSE ); /* Fatal error */
}

/******************************************************************************
 ** Function name:		MCI_Write_Block
 **
 ** Descriptions:		Set MCI data control register, data length and data
 **						timeout, send WRITE_BLOCK cmd, finally, enable
 **						interrupt. On completion of WRITE_BLOCK cmd, TX_ACTIVE
 **						interrupt will occurs, data can be written continuously
 **						into the FIFO until the block data length is reached.
 **
 ** parameters:			block number
 ** Returned value:		true or false, if cmd times out, return false and no
 **						need to continue.
 **
 ******************************************************************************/
DWORD MCI_Write_Block(DWORD blockNum) {
	volatile DWORD i; // mthomas - volatile
	DWORD DataCtrl = 0;

	MCI_CLEAR = 0x7FF;
	MCI_DATA_CTRL = 0;
	for (i = 0; i < 0x10; i++) {
		;
	}
	/* Below status check is redundant, but ensure card is in TRANS state
	 before writing and reading to from the card. */
	if (MCI_CheckStatus() != TRUE) {
		xprintf("MCI_Write_Block: not in trans\n");
		MCI_Send_Stop();
		return ( FALSE );
	}

	MCI_DATA_TMR = DATA_TIMER_VALUE;
	MCI_DATA_LEN = BLOCK_LENGTH;
	MCI_Block_End_Flag = 1;
	MCI_TXEnable();

	if (MCI_Send_Write_Block(blockNum) == FALSE) {
		return ( FALSE );
	}
#if DEBUG_SDCARD_INTERRUPTS
	else {
		xprintf("MCI_Send_Write_Block returned true.\n");
	}
#endif

#if MCI_DMA_ENABLED
	DMA_Move( 0, M2P);
	GPDMA_CH0_CFG |= 0x10001 | (0x00 << 1) | (0x04 << 6) | (0x05 << 11);
	/* Write, block transfer, DMA, and data length */
	DataCtrl |= ((1 << 0) | (1 << 3) | (DATA_BLOCK_LEN << 4));
#else
	/* Write, block transfer, and data length */

	DataCtrl |= ((1 << 0) | (DATA_BLOCK_LEN << 4));
#endif

#if DEBUG_SDCARD_INTERRUPTS
	xprintf("Before DataCtrl set...\n");
#endif
	MCI_DATA_CTRL = DataCtrl;
#if DEBUG_SDCARD_INTERRUPTS
	xprintf("After DataCtrl set...\n");
#endif

	//laebi - not necessary
	for (i = 0; i < 0x10; i++) {
		;
	}

	return ( TRUE );
}

/******************************************************************************
 ** Function name:		MCI_Write_Multiple_Block
 **
 ** Descriptions:		Set MCI data control register, data length and data
 **						timeout, send WRITE_MULTIPLE_BLOCK cmd. Continuously
 **						writes blocks of data. At the end a STOP_TRANSMISSION
 **						marks the end of the datatransfer.
 **
 **
 ** parameters:			block number
 **						number of blocks
 **						first address to the data in the memory
 ** Returned value:		true or false, if cmd times out, return false and no
 **						need to continue.
 **
 ** Added by Lorenz Aebi
 ******************************************************************************/
DWORD MCI_Write_Multiple_Block(DWORD blockNum, BYTE count, const BYTE *buff) {
	volatile DWORD i; // mthomas - volatile
	DWORD DataCtrl = 0;

	MCI_CLEAR = 0x7FF;
	MCI_DATA_CTRL = 0;
	//laebi - not necessary
	//for ( i = 0; i < 0x10; i++ ) { ; }

	/* Below status check is redundant, but ensure card is in TRANS state
	 before writing and reading to from the card. */
	if (MCI_CheckStatus() != TRUE) {
		xprintf("MCI_Wead_Block: not in trans\n");
		MCI_Send_Stop();
		return ( FALSE );
	}

	MCI_DATA_TMR = DATA_TIMER_VALUE;
	MCI_DATA_LEN = BLOCK_LENGTH;
	MCI_Block_End_Flag = 1;
	MCI_TXEnable();
	if (MCI_Send_Write_Multiple_Block(blockNum) == FALSE) {
		return ( FALSE );
	}
	do {
#if MCI_DMA_ENABLED

		// TODO: avoid copy
		memcpy((void *)DMA_SRC, buff, 512);
		buff += 512;
		GPDMA_INT_TCCLR = 0x01;
		GPDMA_INT_ERR_CLR = 0x01;
		GPDMA_CH0_SRC = DMA_SRC;
		GPDMA_CH0_DEST = DMA_MCIFIFO;
		GPDMA_CONFIG = 0x01;
		/* Enable DMA channels, little endian */
		while ( !(GPDMA_CONFIG & 0x01))
			;
		/* The burst size is set to 8, the size is 8 bit too. */
		/* Terminal Count Int enable */
		GPDMA_CH0_CTRL = (DMA_SIZE & 0x0FFF) | (0x04 << 12) | (0x02 << 15)
		| (0x02 << 18) | (0x02 << 21) | (1 << 26) | 0x80000000;
		//DMA_Move( 0, M2P );
		GPDMA_CH0_CFG |= 0x10001 | (0x00 << 1) | (0x04 << 6) | (0x05 << 11);
		/* Write, block transfer, DMA, and data length */
		DataCtrl |= ((1 << 0) | (1 << 3) | (DATA_BLOCK_LEN << 4));
#else
		/* Write, block transfer, and data length */
		DataCtrl |= ((1 << 0) | (DATA_BLOCK_LEN << 4));
#endif
		MCI_DATA_CTRL = DataCtrl;
		for (i = 0; i < 0x10; i++) {
			;
		}
	} while (count--);

	//for (i = 0; i < 0x10; i++) { ; }

	MCI_Send_Stop();

	return ( TRUE );
}

/******************************************************************************
 ** Function name:		MCI_Read_Block
 **
 ** Descriptions:		Set MCI data control register, data length and data
 **						timeout, send READ_SINGLE_BLOCK cmd, finally, enable
 **						interrupt. On completion of READ_SINGLE_BLOCK cmd,
 **						RX_ACTIVE interrupt will occurs, data can be read
 **						continuously into the FIFO until the block data
 **						length is reached.
 **
 ** parameters:			block number
 ** Returned value:		true or false, if cmd times out, return false and no
 **						need to continue.
 **
 ******************************************************************************/
DWORD MCI_Read_Block(DWORD blockNum) {
	volatile DWORD i; // mthomas - volatile
	DWORD DataCtrl = 0;

	MCI_CLEAR = 0x7FF;
	MCI_DATA_CTRL = 0;
	for (i = 0; i < 0x10; i++) {
		;
	}

	/* Below status check is redundant, but ensure card is in TRANS state
	 before writing and reading to from the card. */
	if (MCI_CheckStatus() != TRUE) {
		xprintf("MCI_Read_Block: not in trans\n");
		MCI_Send_Stop();
		return ( FALSE );
	}
	MCI_RXEnable();

	MCI_DATA_TMR = DATA_TIMER_VALUE;
	MCI_DATA_LEN = BLOCK_LENGTH;
	MCI_Block_End_Flag = 1;
	if (MCI_Send_Read_Block(blockNum) == FALSE) {
		return ( FALSE );
	}

#if MCI_DMA_ENABLED
	DMA_Move( 1, P2M);
	GPDMA_CH1_CFG |= 0x10001 | (0x04 << 1) | (0x00 << 6) | (0x06 << 11);
	/* Write, block transfer, DMA, and data length */
	DataCtrl |= ((1 << 0) | (1 << 1) | (1 << 3) | (DATA_BLOCK_LEN << 4));
#else
	/* Read, enable, block transfer, and data length */
	DataCtrl |= ((1 << 0) | (1 << 1) | (DATA_BLOCK_LEN << 4));
#endif
	MCI_DATA_CTRL = DataCtrl;
	for (i = 0; i < 0x10; i++) {
		;
	}

	return ( TRUE );
}

/******************************************************************************
 ** Function name:		MCI_Sd_Status
 **
 ** Descriptions:		reads the 512bit SD_STATUS (3-22) using ACMD13 (4-37)
 **						Card must be in trans-state for this (4-7)
 **						( SD-Status in ReadBlock when MCI_Block_End_Flag==1 )
 **
 ** parameters:			none
 ** Returned value:		true or false
 **
 ** Added by Martin Thomas
 ******************************************************************************/
DWORD MCI_Sd_Status(void) {
	volatile DWORD i;
	DWORD retryCount;
	DWORD respStatus;
	DWORD respValue[4];
	DWORD stuffbits = 0x00000000;
	DWORD DataCtrl = 0;

	if ( (MCI_CardType == CARD_UNKNOWN ) || (MCI_CardType == MMC_CARD )) {
		return ( FALSE ); // unsupported
	}

	MCI_CLEAR = 0x7FF;
	MCI_DATA_CTRL = 0;
	for (i = 0; i < 0x10; i++) {
		;
	}

	/* Below status check is redundant, but ensure card is in TRANS state
	 before writing and reading to from the card. */
	if (MCI_CheckStatus() != TRUE) {
		MCI_Send_Stop();xprintf("Sd_Status: Not in trans-state\n");
		return ( FALSE );
	}

	MCI_RXEnable();

	MCI_DATA_TMR = DATA_TIMER_VALUE;
	MCI_DATA_LEN = BLOCK_LENGTH;
	// MCI_DATA_LEN = SD_BLOCK_LENGTH;
	MCI_Block_End_Flag = 1;
	SDStatRead = 1;

	// "MCI_Send_SD_STATUS":
	retryCount = 0x20;
	while (retryCount > 0) {
		MCI_CLEAR = 0x7FF;
		// prepare for ACMD (with CMD55)
		if (MCI_Send_ACMD() == TRUE) {
			// Send ACMD13
			MCI_SendCmd( SD_STATUS, stuffbits, EXPECT_SHORT_RESP, 0);
			respStatus = MCI_GetCmdResp( SD_STATUS, EXPECT_SHORT_RESP, (DWORD *)&respValue[0]);
			/* it should be in the transfer state, bit 9~12 is 0x0100 and bit 8 is 1 */
			if ( !respStatus && ((respValue[0] & (0x0F << 8)) == 0x0900)) {
				break; /* ready and in TRAN state */
			}
		}
		for (i = 0; i < 0x20; i++) {
			;
		}
		retryCount--;
	}
	if (retryCount == 0) {
		xprintf("Sd_Status: ACMD13 failed\n");
		return ( FALSE ); // error sending ACMD13 or state not trans
	}

#if MCI_DMA_ENABLED
	DMA_Move( 1, P2M);
	GPDMA_CH1_CFG |= 0x10001 | (0x04 << 1) | (0x00 << 6) | (0x06 << 11);
	/* Write, block transfer, DMA, and data length */
	DataCtrl |= ((1 << 0) | (1 << 1) | (1 << 3) | (SD_STATUS_BLOCK_LEN << 4));
#else
	/* Read, enable, block transfer, and data length */
	DataCtrl |= ((1 << 0) | (1 << 1) | ( SD_STATUS_BLOCK_LEN << 4 ));
#endif
	MCI_DATA_CTRL = DataCtrl;
	// mci_debug_xprintf("MCI_SD_Status: DATA_CTRL set\n");
	for (i = 0; i < 0x10; i++) {
		;
	}

	return ( TRUE );
}

/******************************************************************************
 ** Function name:		MCI_Power_Off
 **
 ** Descriptions:		disable power to MCI (Power-Reg)
 **
 ** parameters:			none
 ** Returned value:		none
 **
 ** Added by Martin Thomas - but it does not work on MCB2300
 ******************************************************************************/
void MCI_Power_Off(void) {
	volatile DWORD i;

	// SCS |= (1<<8);
	MCI_POWER = 0;
	for (i = 0; i < 0x100; i++)
		;xprintf( "MCI_POWER 0x%08x\n", MCI_POWER );
}

/*****************************************************************************
 **                            End Of File
 ******************************************************************************/
